刷新整棵树还是某个节点,获取子节点为什么不能做同步重 IO?
结论
能确定范围时,onDidChangeTreeData 只发射那个节点,编辑器会重新要它的子节点。只有根的结构变了才刷新整棵树。getChildren 可以异步,但不能在里面同步读大目录、同步起进程或同步等网络,否则扩展宿主被堵住,所有视图一起卡。
展开
this.emitter.fire(changedNode); // 只刷新这一支
this.emitter.fire(undefined); // 整棵树
局部刷新保留其余节点的展开状态,也少很多次 getChildren。节点如果是新对象,要用稳定 id,编辑器才知道它还是原来那个。
getChildren 返回 ProviderResult,可以是 Promise。异步加载时先返回 Promise,编辑器会显示忙碌。结果回来再渲染孩子。
禁止的是同步阻塞:
fs.readFileSync扫整个仓库execSync等 git- 在循环里同步解大量 JSON
这些调用发生在扩展宿主的主线程上。树一展开,命令、其他插件的补全都得等它返回。
数据量大时:
- 孩子分页或「加载更多」节点
- 缓存一层,文件监视或命令成功后再让对应节点失效
- 搜索用单独的筛选,不要为了过滤把所有叶子一次建出来
根节点很多时,先给一个父节点,不要把十万文件铺在第一层。
容易讲错的点
- 异步不等于可以不取消。用户折叠又展开时,旧请求回来不要把过期孩子写进新节点。
- 每次按键都
fire(undefined),树会闪,展开状态也可能丢。 - 在
getChildren里弹showInformationMessage询问用户,会让取孩子的过程卡在模态框上。